package cranim.graphics;

import cranim.Constants;
import cranim.graphics.models.*;
import cranim.graphics.models.ships.*;
import cranim.simulation.Shot;
import cranim.simulation.SimCase;
import cranim.simulation.Unit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;

/**
 *
 * @author Benedikt
 */
public class Animation implements Constants{
    
    private SimCase simcase;
    private AnimWindow animWindow;
    private ArrayList<Entity> entities = new ArrayList<>();
    private ArrayList<UnitModel> attackerShips = new ArrayList<>();
    private HashMap<UUID, UnitModel> unitmap = new HashMap<>();
    
    public Animation(SimCase simcase){
        this.simcase = simcase;
                                                      
        animWindow = new AnimWindow();
        
        run();
        //init();
        
    }
    
    private void init(){
        //Paint Defender Planet
        entities.add(getPlanetTemp());
        //Create all ships for first round
        addShips(0);
        //Move attacker ships into position
        moveAttackerShips();
        //Shooting in first round
        shootAllRound(0);
        //Make victims of first round explode
        explodeUnits(0);
        
        animWindow.drawAll(entities);
        
    }
    
    private void run(){
        //Paint Defender Planet
        entities.add(getPlanetTemp());
        
        for(int round = 0; round < simcase.getRounds().size()-1; round++){
            
            //only first round
            if(round == 0){
                //Create all ships
                addShips(round);
                //Move attacker ships into position in first round
                moveAttackerShips();
            }
            //Shooting
            shootAllRound(round);
            //Make victims explode
            explodeUnits(round);
            
            animWindow.drawAll(entities);
        }
    }
    
    private PlanetModel getPlanetTemp(){
                
        int planetTemp = simcase.getPlanetTemperature();
        if(planetTemp == COLD){
            return new PlanetColdModel((int)Math.round(ANIM_WIDTH*0.5),0);
        }
        else if(planetTemp == MEDIUM){
            return new PlanetMediumModel((int)Math.round(ANIM_WIDTH*0.5),0);
        }
        else if(planetTemp == HOT){
            return new PlanetHotModel((int)Math.round(ANIM_WIDTH*0.5),0);
        }
        else{
            System.out.println("Critical Error");            
            System.out.println("Planet temperature does not fit");
            return new PlanetMediumModel((int)Math.round(ANIM_WIDTH*0.5),0);
        }
    }
    
    private void addShips(int round){                
        //Add all attacker ships    
        //Small cargos  
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(SCARGO).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(SCARGO).get(unitoftype);
            Entity entity = new AttackerSmallCargoModel(0, 0);
            int count = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(SCARGO).size();
            addShipOfType(unit, entity, 0.45, count, unitoftype, true);
            
        }    
        //Large cargos  
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(LCARGO).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(LCARGO).get(unitoftype);
            Entity entity = new AttackerLargeCargoModel(0, 0);            
            int count = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(LCARGO).size();
            addShipOfType(unit, entity, 0.41, count, unitoftype, true);            
        }
        //Light fighters    
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(LFIGHTER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(LFIGHTER).get(unitoftype);
            Entity entity = new AttackerLightFighterModel(0, 0);            
            int count = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(LFIGHTER).size();
            addShipOfType(unit, entity, 0.38, count, unitoftype, true);            
        }
        //Heavy fighters    
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(HFIGHTER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(HFIGHTER).get(unitoftype);
            Entity entity = new AttackerHeavyFighterModel(0, 0);            
            int count = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(HFIGHTER).size();
            addShipOfType(unit, entity, 0.35, count, unitoftype, true);            
        }
        //CRUISER
        //BSHIP
        
        //Battlecruisers
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(BCRUISER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(BCRUISER).get(unitoftype);
            Entity entity = new AttackerBattlecruiserModel(0, 0);            
            int count = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(BCRUISER).size();
            addShipOfType(unit, entity, 0.22, count, unitoftype, true);            
        }
        //COLSHIP
        
        //Recyclers
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(RECYCLER  ).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(RECYCLER  ).get(unitoftype);
            Entity entity = new AttackerRecyclerModel(0, 0);            
            int count = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(RECYCLER  ).size();
            addShipOfType(unit, entity, 0.2, count, unitoftype, true);            
        }
        //PROBE
        
        //Bombers
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(BOMBER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(BOMBER).get(unitoftype);
            Entity entity = new AttackerBomberModel(0, 0);            
            int count = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(BOMBER).size();
            addShipOfType(unit, entity, 0.18, count, unitoftype, true);            
        }
        //Destroyers
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(DESTROYER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(DESTROYER).get(unitoftype);
            Entity entity = new AttackerDestroyerModel(0, 0);            
            int count = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(DESTROYER).size();
            addShipOfType(unit, entity, 0.25, count, unitoftype, true);   
        }
        //Deathstars
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(DEATHSTAR).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(DEATHSTAR).get(unitoftype);
            Entity entity = new AttackerDeathstarModel(0, 0);            
            int count = simcase.getRound(round).getAttacker().getFleet().getUnitsOfType(DEATHSTAR).size();
            addShipOfType(unit, entity, 0.01, count, unitoftype, true);            
        }  
        
        //Add all defender ships
        //Small cargos
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getDefender().getFleet().getUnitsOfType(SCARGO).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(SCARGO).get(unitoftype);
            Entity entity = new DefenderSmallCargoModel(0, 0);            
            int count = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(SCARGO).size();
            addShipOfType(unit, entity, 0.50, count, unitoftype, false);
        }
        //Large cargos
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getDefender().getFleet().getUnitsOfType(LCARGO).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(LCARGO).get(unitoftype);
            Entity entity = new DefenderLargeCargoModel(0, 0);            
            int count = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(LCARGO).size();
            addShipOfType(unit, entity, 0.53, count, unitoftype, false);
        }
        //Light fighters
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getDefender().getFleet().getUnitsOfType(LFIGHTER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(LFIGHTER).get(unitoftype);
            Entity entity = new DefenderLightFighterModel(0, 0);            
            int count = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(LFIGHTER).size();
            addShipOfType(unit, entity, 0.56, count, unitoftype, false);            
        }
        //Heavy fighters
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getDefender().getFleet().getUnitsOfType(HFIGHTER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(HFIGHTER).get(unitoftype);
            Entity entity = new DefenderHeavyFighterModel(0, 0);            
            int count = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(HFIGHTER).size();
            addShipOfType(unit, entity, 0.59, count, unitoftype, false);
        }
        //CRUISER
        //BSHIP
        
        //Battlecruisers
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getDefender().getFleet().getUnitsOfType(BCRUISER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(BCRUISER).get(unitoftype);
            Entity entity = new DefenderBattlecruiserModel(0, 0);            
            int count = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(BCRUISER).size();
            addShipOfType(unit, entity, 0.71, count, unitoftype, false);
        }
        //COLSHIP
        
        //Recyclers
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getDefender().getFleet().getUnitsOfType(RECYCLER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(RECYCLER).get(unitoftype);
            Entity entity = new DefenderRecyclerModel(0, 0);            
            int count = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(RECYCLER).size();
            addShipOfType(unit, entity, 0.74, count, unitoftype, false);
        }
        //PROBE
        
        //Bombers
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getDefender().getFleet().getUnitsOfType(BOMBER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(BOMBER).get(unitoftype);
            Entity entity = new DefenderBomberModel(0, 0);            
            int count = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(BOMBER).size();
            addShipOfType(unit, entity, 0.8, count, unitoftype, false);
        }
        //Destroyers
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getDefender().getFleet().getUnitsOfType(DESTROYER).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(DESTROYER).get(unitoftype);
            Entity entity = new DefenderDestroyerModel(0, 0);            
            int count = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(DESTROYER).size();
            addShipOfType(unit, entity, 0.65, count, unitoftype, false);
        }
        
        //Deathstars
        for(int unitoftype = 0; unitoftype < simcase.getRound(round).getDefender().getFleet().getUnitsOfType(DEATHSTAR).size(); unitoftype++){
            Unit unit = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(DEATHSTAR).get(unitoftype);
            Entity entity = new DefenderDeathstarModel(0, 0);            
            int count = simcase.getRound(round).getDefender().getFleet().getUnitsOfType(DEATHSTAR).size();
            addShipOfType(unit, entity, 0.8, count, unitoftype, false);
        }
        //SOLSAT
        //RLAUNCHER
        //LLASER
        //HLASER
        //GAUSS
        //IONCAN
        //PLASMATUR
        //SSHIELD
        //LSHIELD
    }
        
    private void addShipOfType(Unit unit, Entity entity, double factorX, int count, int iterator, boolean isAttacker){
        
        int x, y;
        int middleX = (int)(ANIM_WIDTH*factorX);
        int middleY = ANIM_HEIGHT/2-entity.getSprite().getHeight()/2;
        double offsetX = (ANIM_WIDTH*0.1)/(double)count;
        double offsetY = (double)middleY/(double)count;
        int moveAttacker = 0;
        if(isAttacker){
            moveAttacker = ATTACKER_START;
            if(iterator % 2 == 0){
                x = (int)((double)middleX - (double)iterator*offsetX - moveAttacker);
                y = (int)(middleY + iterator*offsetY );
            }
            else{
                x = (int)((double)middleX - (double)iterator*offsetX - moveAttacker);
                y = (int)(middleY - (iterator+1)*offsetY);     
            }
        }
        else{
            if(iterator % 2 == 0){
                x = (int)((double)middleX + (double)iterator*offsetX);
                y = (int)(middleY + iterator*offsetY);
            }
            else{
                x = (int)((double)middleX + (double)iterator*offsetX);
                y = (int)(middleY - (iterator+1)*offsetY);     
            }
        }        
        entity.setCoords(x, y);
        entities.add(entity);
        unitmap.put(unit.getID(), (UnitModel)entity);

        //Add to attacker ships for initial movement
        if(isAttacker){
            attackerShips.add((UnitModel) entity);
        }
    }
    
    private void moveAttackerShips(){
        
        long lastLoopTime = System.currentTimeMillis();
        double moved = 0;
        
        while(moved < ATTACKER_START){
            long deltaTime = System.currentTimeMillis() - lastLoopTime;
            double deltaX = deltaTime*(ATTACKER_START/(double)ATTACKER_MOVEMENT_MILLIS);
            
            for (int i=0;i<attackerShips.size();i++) {
                attackerShips.get(i).move(deltaX);
            }            
            moved += deltaX;
                        
            animWindow.drawAll(entities);
            lastLoopTime = System.currentTimeMillis();
            try{
                Thread.sleep(ANIM_STEP_MILLIS);
            }
            catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
        attackerShips = null;
    }
    public void shootAllRound(int round){

        for(int i = 0; i < simcase.getRound(round).getAttackerShots().size(); i++){          
            Shot shot = simcase.getRound(round).getAttackerShots().get(i);
            fireShot(shot);
        }
        
        for(int i = 0; i < simcase.getRound(round).getDefenderShots().size(); i++){            
            Shot shot = simcase.getRound(round).getDefenderShots().get(i);
            fireShot(shot);
        }
        animWindow.drawAll(entities); 
    }
    
    private void fireShot(Shot shot){
        UnitModel shooter = unitmap.get(shot.getShooter().getID());
        UnitModel target = unitmap.get(shot.getTarget().getID());
        ShotModel shotmodel = null;
        switch(target.getWeapon()){
            case(LASER):        shotmodel = new LaserShotModel(shooter, target);
            case(IONBEAM):      shotmodel = new IonShotModel(shooter, target);
            case(GAUSSP):       shotmodel = new GaussShotModel(shooter, target);
            case(PLASMABEAM):   shotmodel = new PlasmaShotModel(shooter, target);
            case(GRAVITON):     shotmodel = new GravitonShotModel(shooter, target);
        }
        shotmodel.setCoords(shotmodel.getX()-shotmodel.getSprite().getWidth()/2, shotmodel.getY()-shotmodel.getSprite().getHeight()/2);
        entities.add(shotmodel);
        animWindow.drawAll(entities);          

        long lastLoopTime = System.currentTimeMillis();
        while(!shotmodel.hasHit()){
            long deltaTime = System.currentTimeMillis() - lastLoopTime;
            double delta = deltaTime*LASER_SHOT_SPEED;
            shotmodel.move(delta);

            animWindow.drawAll(entities);
            lastLoopTime = System.currentTimeMillis();
            try{
                Thread.sleep(ANIM_STEP_MILLIS);
            }
            catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
        //TODO: draw glowing shields
        entities.remove(shotmodel);
    }
    
    private void explodeUnits(int round){
        //Attacker
        for(int i = 0; i < simcase.getRound(round).getAttacker().getFleet().getSize(); i++){
            Unit unit = simcase.getRound(round).getAttacker().getFleet().getUnits().get(i);
            //System.out.println("Unit "+unit.toString()+" has "+unit.shields+" shields, "+unit.hull+" hull and is "+unit.getDestroyFlag()+" destroyed");
            if(unit.getDestroyFlag()){
                UnitModel destroyedUnit = unitmap.get(unit.getID());
                //TODO: draw explosion
                entities.remove(destroyedUnit);
                animWindow.drawAll(entities); 
            }
        }
        //Defender
        for(int i = 0; i < simcase.getRound(round).getDefender().getFleet().getSize(); i++){
            Unit unit = simcase.getRound(round).getDefender().getFleet().getUnits().get(i);
            //System.out.println("Unit "+unit.toString()+" has "+unit.shields+" shields, "+unit.hull+" hull and is "+unit.getDestroyFlag()+" destroyed");
            if(unit.getDestroyFlag()){
                UnitModel destroyedUnit = unitmap.get(unit.getID());
                //TODO: draw explosion
                entities.remove(destroyedUnit);
                animWindow.drawAll(entities); 
            }
        }
    }
    
}
